﻿namespace FML

#light

module OptionT =
 module List = 
 
  open OptionM
  open ListM

  type IOptionListMonad = interface
     abstract Bind : 'a option list * ('a -> 'b option list) -> 'b option list
     abstract Let : 'a * ('a -> 'b option list) -> 'b option list
     abstract Return : 'a -> 'a option list
     abstract Delay : f:(unit -> 'a option list) -> 'a option list
     abstract Zero : unit -> 'a option list 
     abstract Lift : 'a list -> 'a option list
  end 
 
 
  type OptionListBuilder(listM : IListMonad) = class end
    with interface IOptionListMonad with
                member s.Bind(a, f) =  let f' x = match x with
                                                  |None -> listM.Return(None)
                                                  |Some x' -> f x'
                                       listM { let! a' = a
                                               return! f' a'
                                             }
                member s.Let(a, f) =   f a
                member s.Return a = listM.Return(Some a)
                member s.Delay f = f()
                member s.Zero() = listM.Zero()
                member s.Lift(a) = listM { let! a' = a
                                           return (Some a') }
  
  let optionListT = new OptionListBuilder(listM) :>  IOptionListMonad
   
 module State = 

  open OptionM
  open StateM

  type IOptionStateMonad = interface
     abstract Bind : State<'a option, 'state> * ('a ->  State<'b option, 'state>) -> State<'b option, 'state>
     abstract Let : 'a * ('a ->  State<'b option, 'state>) ->  State<'b option, 'state>
     abstract Return : 'a ->  State<'a option, 'state>
     abstract Delay : f:(unit ->  State<'a option, 'state>) ->  State<'a option, 'state>
     abstract Zero : unit -> State<'a option, 'state>
     abstract Lift : State<'a, 'state> ->  State<'a option, 'state>
  end 
 
 
  type OptionStateBuilder(stateM : IStateMonad) = class end
    with interface IOptionStateMonad with
                member s.Bind(a, f) =  let f' x = match x with
                                                  |None -> stateM.Return(None)
                                                  |Some x' -> f x'
                                       stateM { let! a' = a
                                                return! f' a'
                                              }
                member s.Let(a, f) =   f a
                member s.Return a = stateM.Return(Some a)
                member s.Delay f = f()
                member s.Zero() = stateM.Return(None)
                member s.Lift(a) = stateM { let! a' = a
                                            return (Some a') }
  
  let optionStateT = new OptionStateBuilder(stateM) :>  IOptionStateMonad
 